using AOT;
using System;
using Unity.Burst;

namespace DA.AnimGraph {
    
    [BurstCompile]
    public unsafe struct TransitionConditionLink : IDisposable {

        #region VirtualTable

        // Check
        internal delegate bool CheckDelegate(in void* condition, in NodeLink node, in GraphContext graphContext);

        #endregion

        internal TransitionConditionVirtualTable virtualFunction;
        private void* conditionPtr;
        public ConditionType conditionType;
        public AGRuntimeType perkType;

        public TransitionConditionLink(ConditionType type) {
            conditionType = type;
            conditionPtr = null;
            virtualFunction = default;
            perkType = default;
        }

        internal void SetupCondition(void* conditionPtr) {
            this.conditionPtr = conditionPtr;
        }

        internal bool Check(in NodeLink node, in GraphContext graphContext) {
            return virtualFunction.CheckFunctionCall(conditionPtr, node, graphContext);
        }

        public void Dispose() {
            conditionPtr = null;
        }

        public void Dry(DryParam dryParam) {
            // 1 自己对应的perk
            // DryUtility.SwitchDryNode(perkType, conditionPtr, dryParam);
            // 2 自己内部数据
            DryUtility.DryPtr(ref conditionPtr, dryParam);
        }

        public void Wet(DryParam dryParam) {
            virtualFunction = AGRuntimeTypeRegister.GetInstance().GetTransitionConditionVirtualTable(perkType);
            DryUtility.WetPtr(ref conditionPtr, dryParam);
            // DryUtility.SwitchWetNode(perkType, conditionPtr, dryParam);
        }

    }

    [BurstCompile]
    public unsafe partial struct TransitionCondition_ExitTime {
        public float exitTime;

        [BurstCompile]
        internal static void SetupLink(TransitionCondition_ExitTime* self, ref TransitionConditionLink link) {
            link.SetupCondition(self);
        }

        internal void Setup(in TransitionConditionDesc desc) {
            exitTime = desc.exitTime;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(TransitionConditionLink.CheckDelegate))]
        private static bool Check(in void* condition, in NodeLink node, in GraphContext graphContext) {
            var self = (TransitionCondition_ExitTime*)condition;
            float exitTime = self->exitTime;

            // float time = node.GetTime();
            // if (time < 0) {
            //     // 不是可播放节点就不存在时间，不能按时间判断,当做永远不满足条件
            //     // 正常来说不存在这里退出的情况，这里做兼容处理。应当在编辑数据时报错
            //     return false;
            // }
            var timeState = node.GetTimeState();

            if (exitTime > 0) {
                // return time > exitTime;
                return timeState->GetNormalizedTime() > exitTime;
            } else {
                // float duration = node.GetDuration();
                // exitTime = duration + exitTime;
                // return time > exitTime;
                return timeState->GetNormalizedTime() > (1 + exitTime);
            }
        }
    }


    [BurstCompile]
    public unsafe partial struct TransitionCondition_Blackboard {
        AUID paramKey;
        AUID compareParamKey;
        Number compareValue;
        ConditionParameterType paramType;
        CompareOperator compareType;

        [BurstCompile]
        internal static void SetupLink(TransitionCondition_Blackboard* self, ref TransitionConditionLink link) {
            link.SetupCondition(self);
        }

        internal void Setup(in TransitionConditionDesc desc) {
            paramKey = desc.compare.paramKey;
            compareParamKey = desc.compare.compareParamKey;
            paramType = desc.compare.paramType;
            compareType = desc.compare.compareOperator;
            compareValue = desc.compare.compareValue;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(TransitionConditionLink.CheckDelegate))]
        private static bool Check(in void* condition, in NodeLink node, in GraphContext graphContext) {
            var self = (TransitionCondition_Blackboard*)condition;

            bool result = false;
            switch (self->paramType) {
                case ConditionParameterType.Float: {
                    float paramValue = 0;
                    float compareParamValue = 0;
                    graphContext.blackboard.GetValue(self->paramKey, ref paramValue);
                    if (self->compareParamKey.isValid) {
                        graphContext.blackboard.GetValue(self->compareParamKey, ref compareParamValue);
                    } else {
                        compareParamValue = self->compareValue.floatValue;
                    }

                    result = self->compareType switch {
                        CompareOperator.Equal => paramValue == compareParamValue,
                        CompareOperator.NotEqual => paramValue != compareParamValue,
                        CompareOperator.Greater => paramValue > compareParamValue,
                        CompareOperator.GreaterOrEqual => paramValue >= compareParamValue,
                        CompareOperator.Less => paramValue < compareParamValue,
                        CompareOperator.LessOrEqual => paramValue <= compareParamValue,
                        CompareOperator.True => !paramValue.IsNearZero(),
                        CompareOperator.False => paramValue.IsNearZero(),
                        _ => throw new NotImplementedException(),
                    };
                }
                    break;
                case ConditionParameterType.Int: {
                    int paramValue = 0;
                    int compareParamValue = 0;
                    graphContext.blackboard.GetValue(self->paramKey, ref paramValue);
                    if (self->compareParamKey.isValid) {
                        graphContext.blackboard.GetValue(self->compareParamKey, ref compareParamValue);
                    } else {
                        compareParamValue = self->compareValue.intValue;
                    }

                    result = self->compareType switch {
                        CompareOperator.Equal => paramValue == compareParamValue,
                        CompareOperator.NotEqual => paramValue != compareParamValue,
                        CompareOperator.Greater => paramValue > compareParamValue,
                        CompareOperator.GreaterOrEqual => paramValue >= compareParamValue,
                        CompareOperator.Less => paramValue < compareParamValue,
                        CompareOperator.LessOrEqual => paramValue <= compareParamValue,
                        CompareOperator.True => paramValue != 0,
                        CompareOperator.False => paramValue == 0,
                        _ => throw new NotImplementedException(),
                    };
                }
                    break;
                // case ConditionParameterType.Bool: {
                //     bool paramValue = false;
                //     bool compareParamValue = false;
                //     graphContext.blackboard.GetValue(self->paramKey, ref paramValue);
                //     graphContext.blackboard.GetValue(self->compareParamKey, ref compareParamValue);
                //
                //     result = self->compareType switch {
                //         CompareOperator.Equal => paramValue == compareParamValue,
                //         CompareOperator.NotEqual => paramValue != compareParamValue,
                //         CompareOperator.True => paramValue,
                //         CompareOperator.False => !paramValue,
                //         _ => throw new NotImplementedException(),
                //     };
                // }
                //     break;
            }
            return result;
        }
    }
}
